﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CodeMilkerV2.Repositories;
using CodeMilkerV2.Models;
using System.IO;

namespace CodeMilkerV2 {
    public class CodeMilker {
        public static SourceCode Mike(string filePath, TargetEnum target) {
            var entityRepository = new EntityRepository();

            var entity = entityRepository.GetEntity(filePath);
            string fileName = string.Empty;
            string ext = string.Empty;
            List<string> replaces = new List<string>();
            string initialLower = ToInitialLower(entity.EntityName);
            string lower = entity.EntityName.ToLower();
            string name = entity.EntityName;
            string pairs = GetPropertyPairs(entity, target);

            switch (target) {
                case TargetEnum.SQLScript:
                    replaces.Add(lower); // lower
                    replaces.Add(pairs);// pairs
                    replaces.Add(name); // entity name
                    replaces.Add("Id"); //prikey
                    ext = "SQL文件|*.sql";
                    fileName = string.Format("{0}.sql", name);
                    break;
                case TargetEnum.Entity:
                    replaces.Add(name);// entity name
                    replaces.Add(pairs);// pairs
                    ext = "C#文件|*.cs";
                    fileName = string.Format("{0}.cs", name);
                    break;
                case TargetEnum.IRepository:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower                 
                    ext = "C#文件|*.cs";
                    fileName = string.Format("I{0}Repository.cs", name);
                    break;
                case TargetEnum.Repository:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower       
                    ext = "C#文件|*.cs";
                    fileName = string.Format("{0}Repository.cs", name);
                    break;
                case TargetEnum.ICreateCommand:
                    replaces.Add(name);// entity name
                    replaces.Add(pairs);// pairs
                    ext = "C#文件|*.cs";
                    fileName = string.Format("ICreate{0}Command.cs", name);
                    break;
                case TargetEnum.FormModel:
                    replaces.Add(entity.EntityName);// entity name
                    replaces.Add(pairs);// pairs
                    ext = "C#文件|*.cs";
                    fileName = string.Format("{0}FormModel.cs", name);
                    break;
                case TargetEnum.HandlerCreate:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower   
                    ext = "C#文件|*.cs";
                    fileName = string.Format("Create{0}.cs", name);
                    break;
                case TargetEnum.HandlerDelete:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower   
                    ext = "C#文件|*.cs";
                    fileName = string.Format("Delete{0}.cs", name);
                    break;
                case TargetEnum.HandlerRead:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower   
                    ext = "C#文件|*.cs";
                    fileName = string.Format("Get{0}.cs", name);
                    break;
                case TargetEnum.HandlerUpdate:
                    replaces.Add(name);
                    replaces.Add(initialLower);// lower   
                    ext = "C#文件|*.cs";
                    fileName = string.Format("Update{0}.cs", name);
                    break;
                case TargetEnum.ToUnity:
                    replaces.Add(entity.EntityName);
                    break;
                case TargetEnum.ToDbContext:
                    replaces.Add(name);
                    replaces.Add(lower);
                    break;
                case TargetEnum.ToEntity:
                    replaces.Add(name);
                    replaces.Add(pairs);
                    break;
                case TargetEnum.ToEditor:
                    replaces.Add(pairs);
                    break;
                case TargetEnum.ToFormModel:
                    replaces.Add(pairs);
                    replaces.Add(name);
                    break;
                case TargetEnum.ToViewModel:
                    break;
            }

            // 获取目标模板内容
            string template = GetTemplateContent(target.ToString());

            // 以替换内容的方式生成实际代码
            var sourceCode = new SourceCode();
            sourceCode.FileName = fileName;
            sourceCode.Ext = ext;
            sourceCode.FileContent = string.Format(template, replaces.ToArray());

            return sourceCode;
        }





        private static string GetPropertyPairs(Entity entity, TargetEnum target) {

            StringBuilder sb = new StringBuilder();

            var nullMark = "";
            foreach (var attr in entity.Attributes) {
                if (attr.DataType.CsType.ToLower() == "string" || attr.DataType.CsType.ToLower() == "byte[]") {
                    nullMark = "";
                }
                else {
                    nullMark = attr.IsNullable ? "?" : "";
                }
                switch (target) {
                    case TargetEnum.Entity:
                        sb.Append(string.Format("public {0}{1} {2}{{get;set;}}", attr.DataType.CsType, nullMark, attr.Name)).Append("\n");
                        break;
                    case TargetEnum.SQLScript:
                        nullMark = attr.IsNullable == true ? "Null" : "Not Null";
                        var dbType = attr.DataType.DbType.ToLower();
                        if (dbType.Equals("nvarchar")) {// nvarchar
                            sb.Append(string.Format("[{0}] [{1}]({2}) {3},", attr.Name, dbType, attr.Length, nullMark)).Append("\n");
                        }
                        else if (dbType.Equals("decimal")) { // decimal
                            sb.Append(string.Format("[{0}] [{1}]({2},{3}) {4},", attr.Name, dbType, attr.Precision, attr.Scale, nullMark)).Append("\n");
                        }
                        else if (dbType.Equals("nvarchar(max)")) {// nvarchar(max)
                            sb.Append(string.Format("[{0}] [nvarchar](max) {1},", attr.Name, nullMark)).Append("\n");
                        }
                        else {// others
                            sb.Append(string.Format("[{0}] [{1}] {2},", attr.Name, dbType, nullMark)).Append("\n");
                        }
                        break;
                    case TargetEnum.FormModel:
                        if (attr.IsEdit == true) {
                            var display = string.Format("[Display(Name = \"{0}\")]", attr.Label);
                            var required = string.Empty;
                            var allowValidation = string.Empty;
                            var property = string.Empty;

                            sb.Append(display).Append("\n");
                            if (attr.Required == true) {
                                required = string.Format("[Required(ErrorMessage = \"请填写{0}\")]", attr.Label);
                                sb.Append(required).Append("\n");
                            }

                            if (attr.Allow != null) {
                                switch (attr.AllowCondition) {
                                    case "String":
                                        if (string.IsNullOrWhiteSpace(attr.MinVal) && !String.IsNullOrWhiteSpace(attr.MaxVal)) {
                                            allowValidation = string.Format(" [StringLength({0}, ErrorMessage = \"请输入有效{1}\")]", attr.MaxVal, attr.Label);
                                        }
                                        else if (!string.IsNullOrWhiteSpace(attr.MinVal) && String.IsNullOrWhiteSpace(attr.MaxVal)) {
                                            allowValidation = string.Format(" [StringLength(MinimumLength={0}, ErrorMessage = \"请输入有效{1}\")]", attr.MinVal, attr.Label);
                                        }
                                        else if (!string.IsNullOrWhiteSpace(attr.MinVal) && !String.IsNullOrWhiteSpace(attr.MaxVal)) {
                                            allowValidation = string.Format(" [StringLength({0},MinimumLength={1}, ErrorMessage = \"请输入有效{2}\")]", attr.MaxVal, attr.MinVal, attr.Label);
                                        }
                                        break;
                                    case "Int":
                                    case "Decimal":
                                        allowValidation = string.Format(" [Range({0},{1}, ErrorMessage = \"请输入有效{2}\")]", attr.MinVal, attr.MaxVal, attr.Label);
                                        break;
                                    case "Date":
                                        allowValidation = string.Format(" [Range(typeof(DateTime),\"{0}\",\"{1}\", ErrorMessage = \"请输入有效{2}\")]", attr.MinVal, attr.MaxVal, attr.Label);
                                        break;
                                }

                                if (!string.IsNullOrWhiteSpace(allowValidation)) {
                                    sb.Append(allowValidation).Append("\n");
                                }
                            }

                            property = string.Format("public {0}{1} {2}{{get;set;}}", attr.DataType.CsType, nullMark, attr.Name);
                            sb.Append(property).Append("\n\n");
                        }
                        break;
                    case TargetEnum.ICreateCommand:
                        if (attr.IsEdit == true) {
                            sb.Append(string.Format("{0}{1} {2}{{get;set;}}", attr.DataType.CsType, nullMark, attr.Name)).Append("\n");
                        }
                        break;
                    case TargetEnum.ToEntity:
                        sb.Append(string.Format("entity.{0} = formModel.{0};", attr.Name)).Append("\n");
                        break;
                    case TargetEnum.ToFormModel:
                        if (attr.IsEdit == true) {
                        sb.Append(string.Format("{0} = entity.{0},",attr.Name)).Append("\n");
                        }
                        break;
                    case TargetEnum.ToEditor:
                        if (attr.IsEdit == true) {
                            var label = string.Format("@Html.LabelFor(model => model.{0})", attr.Name);
                            var field = (attr.DataType.Name == "lov") ? string.Format("@Html.DropDownListFor(model => model.{0},null,\"请选择\")", attr.Name) : string.Format("@Html.TextBoxFor(model => model.{0})", attr.Name);
                            var validation = string.Empty;

                            if (attr.Required == true || attr.AllowCondition != null) {
                                validation = string.Format("@Html.ValidationMessageFor(model => model.{0})", attr.Name);
                            }

                            sb.Append(string.Format("<tr><td class=\"editor-label\">{0}</td><td class=\"editor-field\">{1}{2}</td></tr>", label, field, validation));
                        }
                        break;

                }
            }

            return sb.ToString();
        }

        private static string GetTemplateContent(string templateName) {
            string path = string.Format("{0}/Templates/{1}.txt", AppDomain.CurrentDomain.BaseDirectory, templateName);

            using (StreamReader sr = new StreamReader(path)) {
                return sr.ReadToEnd();
            }
        }

        private static string ToInitialLower(string src) {
            string initial = src.Substring(0, 1).ToLower();
            string leave = src.Substring(1, src.Length - 1);

            return initial + leave;
        }
    }

    public class SourceCode {
        public string FileName { get; set; }
        public string Ext { get; set; }
        public string FileContent { get; set; }
    }
}
